/* header files */
#include "bufferqueue_02.h"

/* function declaration */
int bufque_init(bufque_t* que, size_t quesize,size_t elemsize);
int bufque_destroy(bufque_t* que);
static ssize_t place(bufque_t* que, void* elem);
static void* fetch(bufque_t* que);
static void* peek(bufque_t* que);
static size_t getsize(bufque_t* que);

int bufque_init(bufque_t* que, size_t quesize,size_t elemsize){
    que->array = (void**)malloc(quesize * sizeof(void*));
    que->capacity = quesize;   
    que->elemsize = elemsize;    // 队列元素所占内存字节数

    que->head = 0;  
    que->tail = 0;  // always empty
    que->size = 0;  // elem count

    sem_init(&que->lock, 0, 1);
    sem_init(&que->full, 0, 0);     
    sem_init(&que->empty, 0, que->capacity);    
    
    que->enque = place;     // not thread safe
    que->deque = fetch;     // not thread safe
    que->peek = peek;       // thread safe
    que->getsize = getsize; // thread safe
    
    printf("buffer queue initialized\n");
    
    return 0;
}

int bufque_destroy(bufque_t* que)
{
    for(size_t i=0; i<que->capacity; i++){
        free(que->array[i]);
    }
    free(que->array);
    sem_destroy(&que->lock);
    sem_destroy(&que->full);
    sem_destroy(&que->empty);
    return 0;
}

static void* fetch(bufque_t* que)
{// 不保证队列不溢出 && 完全的内存不安全
 // 这个地方最麻烦的地方在于函数返回了一个指向堆
 // 上内存的指针，要求在函数外部做内存管理
    void* ret = malloc(sizeof(que->elemsize));
    assert(ret!=NULL);
    
    memcpy(ret, que->array[que->head], que->elemsize);
    que->head = (que->head+1) % (que->capacity);
    --que->size;

    return ret;
}

static ssize_t place(bufque_t* que, void* elem)
{// 不保证队列不被击穿
 // 复制的时候将元素完整复制至newelem，将&newelem存储至队列中
    ssize_t ret;
    void* temp = malloc(sizeof(que->elemsize));
    assert(temp!=NULL);
    
    memcpy(temp, elem, que->elemsize);
    que->array[que->tail] = temp;
    que->tail = (que->tail+1)%(que->capacity);
    ret = ++que->size; 
    
    return ret;
}

static void* peek(bufque_t* que)
{
    void* ret = (void*)malloc(que->elemsize);
    assert(ret != NULL);

    sem_wait(&que->lock); 
    memcpy(ret, que->array[que->head], que->elemsize);
    sem_post(&que->lock);

    return ret;
};

static size_t getsize(bufque_t* que)
{
    size_t ret;

    sem_wait(&que->lock);
    ret = que->size;
    sem_post(&que->lock);

    return ret;
    
};
